﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Plugins.Windows;
using System.Xml.XPath;
using System.Windows.Documents;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;

namespace XPathPlugin
{
    class XPathHighlighter
    {
        #region Variables

        private bool _initialized;

        private Dictionary<XmlNode, TreeListViewItem> _nodeMapping;
        private Dictionary<XPathExpression, XmlNodeList> _expressionMapping;
        private Dictionary<XPathExpression, List<TextPointer[]>> _textMapping;
        private TreeListView _view;
        private RichTextBox _expressionBox;

        private static readonly Brush _WhiteBrush = new SolidColorBrush(Colors.White);
        private static readonly Brush _HighlightBrush = Brushes.Orange;
        private static readonly Brush _BreakpointBrush = Brushes.Red;

        private bool _itemsCleared;
        private bool _textCleared;

        #endregion

        public XPathHighlighter(Dictionary<XmlNode, TreeListViewItem> nodeMapping, 
                                Dictionary<XPathExpression, XmlNodeList> expressionMapping, 
                                Dictionary<XPathExpression, List<TextPointer[]>> textMapping,
                                TreeListView view,
                                RichTextBox expressionBox
                                )
        {
            _itemsCleared = false;
            _textCleared = false;

            _nodeMapping = nodeMapping;
            _expressionMapping = expressionMapping;
            _textMapping = textMapping;
            _view = view;
            _expressionBox = expressionBox;

            _initialized = (nodeMapping != null && expressionMapping != null && textMapping != null && view != null && expressionBox != null);
        }

        /// <summary>
        /// Highlights whole expression, not check breakpoints
        /// </summary>
        public void HighlightAll() 
        {
            // Highlight last (whole) expression
            Highlight(_expressionMapping.Keys.ElementAt<XPathExpression>(_expressionMapping.Keys.Count - 1), false);
        }

        /// <summary>
        /// Highlight defined expression, not check breakpoints
        /// </summary>
        /// <param name="expression">expression to highlight</param>
        public void Highlight(XPathExpression expression)
        {
            Highlight(expression, false);
        }

        /// <summary>
        /// Highlight defined expression
        /// </summary>
        /// <param name="expression">expression to highlight</param>
        /// <param name="checkBreakpoints">check breakpoints during highlighting</param>
        public void Highlight(XPathExpression expression, bool checkBreakpoints)
        {            
            if (!_initialized)
                return;

            RemoveHighlights();
            
            // Highlight implementation
            HighlightImpl(expression, checkBreakpoints);            
        }
        
        /// <summary>
        /// Remove highlights in expression box and view
        /// </summary>
        public void RemoveHighlights()
        {
            // Performance
            if (!_initialized || (_itemsCleared && _textCleared))
                return;

            _itemsCleared = _textCleared = true;

            RemoveTextHighlights();

            RemoveTreeViewHighlights();            
        }        

        /// <summary>
        /// Implementation of highlighting, highlights text and view
        /// </summary>
        /// <param name="expression">expression to highlight</param>
        /// <param name="checkBreakPoints">if true, breapoint are colored red in view</param>
        private void HighlightImpl(XPathExpression expression, bool checkBreakPoints)
        {            
            TextRange textRange;
            bool setFocusBP = false;
            bool setFocusItem = false;
                                             
            // Performace
            _itemsCleared = _textCleared = false;
                
            _view.BeginInit();
            _expressionBox.BeginChange();

            // Text highlighting           
            foreach (TextPointer[] pointerArray in _textMapping[expression])
            {
                textRange = new TextRange(pointerArray[0], pointerArray[1]);

                textRange.ApplyPropertyValue(TextElement.ForegroundProperty, _HighlightBrush);
                textRange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
            }            

            // View highlighting
            foreach (XmlNode node in _expressionMapping[expression])
            {
                try
                {
                    TreeListViewItem viewItem = _nodeMapping[node];

                    // Highlight breakpoints red
                    if (checkBreakPoints && viewItem.IsChecked)
                    {
                        if (!setFocusBP)
                        {
                            setFocusBP = viewItem.Focus();
                            setFocusBP = true;
                        }

                        viewItem.IsSelected = false;
                        viewItem.Background = _BreakpointBrush;
                    }

                    else
                    {
                        if (!setFocusItem && !setFocusBP)
                        {
                            viewItem.Focus();
                            setFocusItem = true;
                        }

                        viewItem.IsSelected = false;
                        viewItem.Background = _HighlightBrush;
                    }
                }
                catch (Exception) { }
            }

            _expressionBox.EndChange();           
            _view.EndInit();            
        }

        /// <summary>
        /// Removes highlights from expression box
        /// </summary>
        private void RemoveTextHighlights()
        {            
            _expressionBox.BeginChange();

            // Get range for whole document
            TextRange textRange = new TextRange(_expressionBox.Document.ContentStart, _expressionBox.Document.ContentEnd);

            // Set default graphics
            textRange.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Black);
            textRange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Normal);            

            _expressionBox.EndChange();
        }

        /// <summary>
        /// Removes highlights from TreeListView nodes
        /// </summary>
        private void RemoveTreeViewHighlights()
        {
            _view.BeginInit();

            _WhiteBrush.Opacity = 0;

            // ForEach node in TreeListView collection
            foreach (TreeListViewItem treeViewItem in _nodeMapping.Values)
                treeViewItem.Background = _WhiteBrush;
            
            _view.EndInit();

        }        
    }
}
